19/05/2024 - 25/05/2024

20/05/2024 09:42

I was able to "revive" the event builder by basically just using the correct flags:

  1. I was able to simply make it in our normal environment
    cd $GM2DAQ_DIR/eventbuilder
    make clean
    make
    cd $GM2DAQ_DIR/eventbuilder
    make clean
    make
  2. After a while of playing around, I figured out I had to run it with these parameters:
    ./mevb -e DAQ -b BUF
    ./mevb -e DAQ -b BUF
    the -b BUF is important. Basically it looks for frontends that write to anything that begin with BUF, so our AMC13001, AMC13002, and MasterGM2 write to BUF001, BUF002, and BUF respectively so they all get compounded into the event builder.
  3. I set the ODB programs settings to run this command:
    screen -dmS event_builder $GM2DAQ_DIR/eventbuilder/mevb -e DAQ -b BUF
    screen -dmS event_builder $GM2DAQ_DIR/eventbuilder/mevb -e DAQ -b BUF
  4. After starting a run, you can then check the output on the SYSTEM buffer (the buffer name can be changed in ebuser.cpp if you'd like):
    $MIDASSYS/bin/mdump -z SYSTEM
    $MIDASSYS/bin/mdump -z SYSTEM

Now all the data will be logged if the logger is set to log the system buffer.
Note:
At one point I tried changing the ODB setting /Equipment/EB/Settings/User build to yes. This broke things, keep it at no.


20/05/2024 09:49

I noticed you will get CCC run abort errors if you try to start the 1 crate system with FC7 SFP ports enabled to talk to more than the one crate you have connected.


20/05/2024 10:19

According to this link: https://daq00.triumf.ca/MidasWiki/index.php/MIDAS_Event_Construction

Midas bank names can only be 4 characters.We were trying to make them 5, I'm not sure if this is problematic for Lawrence's software.


20/05/2024 10:29

To move files using an SSH tunnel:
In one terminal:

ssh -L 2222:10.0.0.3:22 pioneer@10.47.95.44
ssh -L 2222:10.0.0.3:22 pioneer@10.47.95.44

Then:

scp -P 2222 root@localhost:{file to copy} {destination}
scp -P 2222 root@localhost:{file to copy} {destination}

similarly you can reverse the last command to move files.


20/05/2024 12:35

I was able to get interrupt triggering to "work" by adding some things.

To CaloReadoutAMC13/frontend.cpp

//function pointer to the interrupt routine
#define USE_INTERRUPT

#ifdef USE_INTERRUPT
void (*interrupt_routine)(void);
#endif
//function pointer to the interrupt routine
#define USE_INTERRUPT

#ifdef USE_INTERRUPT
void (*interrupt_routine)(void);
#endif
  EQUIPMENT equipment[] = { 
    {
      "AMC13%03d",              /* equipment name */
      {1, 0xffff,               /* event ID, trigger mask */
       "BUF%03d",               /* event buffer */
       #ifdef USE_INTERRUPT
        EQ_INTERRUPT | EQ_EB,            /* equipment type */
       #else
        EQ_POLLED | EQ_EB,               /* equipment type */
       #endif
       LAM_SOURCE(0, 0xFFFFFF), /* event source crate 0, all stations */
       "MIDAS",                 /* format */
       TRUE,                    /* enabled */
       RO_RUNNING ,              /* read only when running and end of run */
       10,                      /* poll for 1ms */
       0,                       /* stop run after this event limit */
       0,                       /* number of sub events */
       0,                       /* don't log history */
       "", "", "",              /* frontend host, frontend name, frontend file name */
       "", "", FALSE},          /* status, status color, hidden */
      read_trigger_event,       /* readout routine */
    },
    {""}
  };
  EQUIPMENT equipment[] = { 
    {
      "AMC13%03d",              /* equipment name */
      {1, 0xffff,               /* event ID, trigger mask */
       "BUF%03d",               /* event buffer */
       #ifdef USE_INTERRUPT
        EQ_INTERRUPT | EQ_EB,            /* equipment type */
       #else
        EQ_POLLED | EQ_EB,               /* equipment type */
       #endif
       LAM_SOURCE(0, 0xFFFFFF), /* event source crate 0, all stations */
       "MIDAS",                 /* format */
       TRUE,                    /* enabled */
       RO_RUNNING ,              /* read only when running and end of run */
       10,                      /* poll for 1ms */
       0,                       /* stop run after this event limit */
       0,                       /* number of sub events */
       0,                       /* don't log history */
       "", "", "",              /* frontend host, frontend name, frontend file name */
       "", "", FALSE},          /* status, status color, hidden */
      read_trigger_event,       /* readout routine */
    },
    {""}
  };

Above is justt a "switch" to turn on and off interrupt

INT interrupt_configure(INT cmd, INT source __attribute__((unused)), POINTER_T adr __attribute__((unused)))
{
  switch (cmd) {
    case CMD_INTERRUPT_ENABLE:
      break;
    case CMD_INTERRUPT_DISABLE:
      break;
    case CMD_INTERRUPT_ATTACH:
      interrupt_routine = (void (*)())adr;
      break;
    case CMD_INTERRUPT_DETACH:
      break;
  }
INT interrupt_configure(INT cmd, INT source __attribute__((unused)), POINTER_T adr __attribute__((unused)))
{
  switch (cmd) {
    case CMD_INTERRUPT_ENABLE:
      break;
    case CMD_INTERRUPT_DISABLE:
      break;
    case CMD_INTERRUPT_ATTACH:
      interrupt_routine = (void (*)())adr;
      break;
    case CMD_INTERRUPT_DETACH:
      break;
  }

I think this is some midas function that somehow links interrupt_routine to read_trigger_event? I'm not sure, I more or less copied it from the master frontend.cpp.

BOOL send_interrupt_signal()
{
  //std::cout << "Received interrupt signal" << std::endl;
  #ifdef USE_INTERRUPT
    (*interrupt_routine)();
  #endif
  return TRUE;
}
BOOL send_interrupt_signal()
{
  //std::cout << "Received interrupt signal" << std::endl;
  #ifdef USE_INTERRUPT
    (*interrupt_routine)();
  #endif
  return TRUE;
}

The actual "signal" used by gpu_thread.cpp

frontend.h

extern void (*interrupt_routine)(void); // Declare the interrupt routine as extern

// Function prototypes
BOOL send_interrupt_signal();
extern void (*interrupt_routine)(void); // Declare the interrupt routine as extern

// Function prototypes
BOOL send_interrupt_signal();

Needed to give gpu_thread.cpp a way to send the interupt signal so it just includes this header file.

gpu_thread.cpp

    send_interrupt_signal();
  } // while (1)
    send_interrupt_signal();
  } // while (1)

If the gpu_thread gets to the end of it's while loop it sends an interrupt signal.

This seems to work for low rates (tested 20 Hz and 200 Hz). But the TCP buffer fills up immediately once I try 1000Hz. Somehow it's worse than polling.

Here's some additional reading on using interrupts:
https://daq00.triumf.ca/MidasWiki/index.php/Frontend_user_code


23/05/2024 02:27

For the parallel port PCIe card.

I downlaoded the drivers here:
https://www.startech.com/en-us/cards-adapters/pex1s1p950

In there was a pdf "PEX1P2 DIG.pdf" that was useful.
PEX1P2 DIG.pdf

I followed those instructions and noticed I didn't see the card in lscpi -v as described. I swapped the PCIe ports of the meineberg and the parallel port PCIe cards and found that the port was the issue; afterwards I didn't see the meinberg but I saw the parrallel port. I'm not sure why this is. I've taken the meinberg card out to avoid further confusion.


23/05/2024 02:39

The pdf says to look at this information in lspci -v:

02:00.2 Parallel controller: Asix Electronics Corporation Device 9100 (prog-if 03 [IEEE1284])
        Subsystem: Device a000:2000
        Flags: fast devsel, IRQ 5
        I/O ports at e010 [disabled] [size=8]
        I/O ports at e000 [disabled] [size=8]
        Memory at fbb01000 (32-bit, non-prefetchable) [disabled] [size=4K]
        Memory at fbb00000 (32-bit, non-prefetchable) [disabled] [size=4K]
        Capabilities: [50] MSI: Enable- Count=1/8 Maskable- 64bit+
        Capabilities: [78] Power Management version 3
        Capabilities: [80] Express Legacy Endpoint, MSI 00
        Capabilities: [100] Advanced Error Reporting
02:00.2 Parallel controller: Asix Electronics Corporation Device 9100 (prog-if 03 [IEEE1284])
        Subsystem: Device a000:2000
        Flags: fast devsel, IRQ 5
        I/O ports at e010 [disabled] [size=8]
        I/O ports at e000 [disabled] [size=8]
        Memory at fbb01000 (32-bit, non-prefetchable) [disabled] [size=4K]
        Memory at fbb00000 (32-bit, non-prefetchable) [disabled] [size=4K]
        Capabilities: [50] MSI: Enable- Count=1/8 Maskable- 64bit+
        Capabilities: [78] Power Management version 3
        Capabilities: [80] Express Legacy Endpoint, MSI 00
        Capabilities: [100] Advanced Error Reporting

I'm pretty confident that the I/O ports should not say "disabled." I'm unsure why this is happening.

Specifically:

        I/O ports at e010 [disabled] [size=8]
        I/O ports at e000 [disabled] [size=8]
        I/O ports at e010 [disabled] [size=8]
        I/O ports at e000 [disabled] [size=8]

And then use that information to run this command:

sudo modprobe parport_pc io=0xe010
sudo modprobe parport_pc io=0xe010

There are no complaints, but I don't know which of these is the correct lp port (if any)

[root@localhost ~]# ls /dev | grep "lp"
lp0
lp1
lp2
lp3
[root@localhost ~]#
[root@localhost ~]# ls /dev | grep "lp"
lp0
lp1
lp2
lp3
[root@localhost ~]#

I tried looking at dmesg, but it's unclear what is going on here (this output occurs rigtht after command sudo modprobe parport_pc io=0xe010)

[root@localhost ~]# dmesg | grep parport
[  279.843630] parport 0xe010 (WARNING): CTR: wrote 0x0c, read 0xff
[  279.843636] parport 0xe010 (WARNING): DATA: wrote 0xaa, read 0xff
[  279.843638] parport 0xe010: You gave this address, but there is probably no parallel port there!
[  279.843652] parport0: PC-style at 0xe010, irq 0 [PCSPP,TRISTATE]
[  279.843659] genirq: Flags mismatch irq 0. 00000000 (parport0) vs. 00015a20 (timer)
[  279.843692]  [<ffffffffc0d7fd90>] ? parport_register_device+0x2d0/0x2d0 [parport]
[  279.843700]  [<ffffffffc0d92d1a>] parport_pc_probe_port+0x61a/0xab0 [parport_pc]
[  279.843704]  [<ffffffffc0d9a0e7>] ? parport_parse_param.constprop.11+0xe7/0xe7 [parport_pc]
[  279.843707]  [<ffffffffc0d9a319>] parport_pc_init+0x232/0xf19 [parport_pc]
[  279.843711]  [<ffffffffc0d9a0e7>] ? parport_parse_param.constprop.11+0xe7/0xe7 [parport_pc]
[  279.843734] parport0: irq 0 in use, resorting to polled operation
[root@localhost ~]# dmesg | grep parport
[  279.843630] parport 0xe010 (WARNING): CTR: wrote 0x0c, read 0xff
[  279.843636] parport 0xe010 (WARNING): DATA: wrote 0xaa, read 0xff
[  279.843638] parport 0xe010: You gave this address, but there is probably no parallel port there!
[  279.843652] parport0: PC-style at 0xe010, irq 0 [PCSPP,TRISTATE]
[  279.843659] genirq: Flags mismatch irq 0. 00000000 (parport0) vs. 00015a20 (timer)
[  279.843692]  [<ffffffffc0d7fd90>] ? parport_register_device+0x2d0/0x2d0 [parport]
[  279.843700]  [<ffffffffc0d92d1a>] parport_pc_probe_port+0x61a/0xab0 [parport_pc]
[  279.843704]  [<ffffffffc0d9a0e7>] ? parport_parse_param.constprop.11+0xe7/0xe7 [parport_pc]
[  279.843707]  [<ffffffffc0d9a319>] parport_pc_init+0x232/0xf19 [parport_pc]
[  279.843711]  [<ffffffffc0d9a0e7>] ? parport_parse_param.constprop.11+0xe7/0xe7 [parport_pc]
[  279.843734] parport0: irq 0 in use, resorting to polled operation

I also tried checking each 'lp' in /dev, but I'm not really sure what output I should see:

[root@localhost ~]# udevadm info -a -p /dev/lp0
syspath not found
[root@localhost ~]# udevadm info -a -p /dev/lp1
syspath not found
[root@localhost ~]# udevadm info -a -p /dev/lp2
syspath not found
[root@localhost ~]# udevadm info -a -p /dev/lp3
syspath not found
[root@localhost ~]# udevadm info -a -p /dev/lp0
syspath not found
[root@localhost ~]# udevadm info -a -p /dev/lp1
syspath not found
[root@localhost ~]# udevadm info -a -p /dev/lp2
syspath not found
[root@localhost ~]# udevadm info -a -p /dev/lp3
syspath not found

It seems like I have to somehow get the parallel controller I/O ports and memory to not be disabled.


23/05/2024 03:15

The readme (readme.txt) mentions

Loading the Driver:
-------------------
    
    To load the driver use the following command:
        $ insmod ax99100.ko

    **  '$' --this symbol represent the shell prompt on linux			

Unloading the Driver:
---------------------

    To unload the driver use the following command:
        $rmmod ax99100

    **  '$' --this symbol represent the shell prompt on linux	
Loading the Driver:
-------------------
    
    To load the driver use the following command:
        $ insmod ax99100.ko

    **  '$' --this symbol represent the shell prompt on linux			

Unloading the Driver:
---------------------

    To unload the driver use the following command:
        $rmmod ax99100

    **  '$' --this symbol represent the shell prompt on linux	

which was actually the first thing I did. I tried unloading the driver, rebooting, and repeating the steps in the pdf above, but no luck.


23/05/2024 03:19

I additionally found these steps in the readme.

Steps for setting parallel port :
---------------------------------
        1. rmmod lp
        2. rmmod parport_pc
        2. insmod parport_pc.ko io=bar0 io_hi=bar1 irq=number.

Note: Here, the io, io_hi and irq should be noted from lspci -v.
Steps for setting parallel port :
---------------------------------
        1. rmmod lp
        2. rmmod parport_pc
        2. insmod parport_pc.ko io=bar0 io_hi=bar1 irq=number.

Note: Here, the io, io_hi and irq should be noted from lspci -v.

So I tried using this information:

02:00.2 Parallel controller: Asix Electronics Corporation Device 9100 (prog-if 03 [IEEE1284])
        Subsystem: Device a000:2000
        Flags: fast devsel, IRQ 5
        I/O ports at e010 [disabled] [size=8]
        I/O ports at e000 [disabled] [size=8]
        Memory at fbb01000 (32-bit, non-prefetchable) [disabled] [size=4K]
        Memory at fbb00000 (32-bit, non-prefetchable) [disabled] [size=4K]
        Capabilities: [50] MSI: Enable- Count=1/8 Maskable- 64bit+
        Capabilities: [78] Power Management version 3
        Capabilities: [80] Express Legacy Endpoint, MSI 00
        Capabilities: [100] Advanced Error Reporting
02:00.2 Parallel controller: Asix Electronics Corporation Device 9100 (prog-if 03 [IEEE1284])
        Subsystem: Device a000:2000
        Flags: fast devsel, IRQ 5
        I/O ports at e010 [disabled] [size=8]
        I/O ports at e000 [disabled] [size=8]
        Memory at fbb01000 (32-bit, non-prefetchable) [disabled] [size=4K]
        Memory at fbb00000 (32-bit, non-prefetchable) [disabled] [size=4K]
        Capabilities: [50] MSI: Enable- Count=1/8 Maskable- 64bit+
        Capabilities: [78] Power Management version 3
        Capabilities: [80] Express Legacy Endpoint, MSI 00
        Capabilities: [100] Advanced Error Reporting

I'm not sure what io and io_hi should be. I tried both:

insmod parport_pc.ko io=0xe010 io_hi=0xe000 irq=5
insmod parport_pc.ko io=0xe010 io_hi=0xe000 irq=5

which takes io_hi to be the second I/O port BAR, and

insmod parport_pc.ko io=0xe010 io_hi=0xe050 irq=5
insmod parport_pc.ko io=0xe010 io_hi=0xe050 irq=5

which adds 8 bytes (the size) listed above to the io BAR.

All of these (and more dumb ideas I tried) seem to result in:

[root@localhost AX99100_SP_PP_SPI_Linux_Driver_v1.8.0_Source]# insmod parport_pc.ko io=0xe010 io_hi=0xe000 irq=5
insmod: ERROR: could not insert module parport_pc.ko: Unknown symbol in module
[root@localhost AX99100_SP_PP_SPI_Linux_Driver_v1.8.0_Source]# insmod parport_pc.ko io=0xe010 io_hi=0xe000 irq=5
insmod: ERROR: could not insert module parport_pc.ko: Unknown symbol in module

At first I thought it was a kernel version issue, but the readme claims no issue:

Kernels:
--------

    This driver is currently developed and tested on 2.6.13 linux kernel and above 
Kernels:
--------

    This driver is currently developed and tested on 2.6.13 linux kernel and above 

as our kernel version is much higher:

[root@localhost AX99100_SP_PP_SPI_Linux_Driver_v1.8.0_Source]# uname -r
3.10.0-1160.99.1.el7.x86_64
[root@localhost AX99100_SP_PP_SPI_Linux_Driver_v1.8.0_Source]# uname -r
3.10.0-1160.99.1.el7.x86_64

24/05/2024 01:35

This is the procedureI followed after installing the driver (see above pdf) to "activate" the card:

  1. Enable the PCIe device:
    echo 1 | sudo tee /sys/bus/pci/devices/0000:02:00.2/enable
    echo 1 | sudo tee /sys/bus/pci/devices/0000:02:00.2/enable
  2. Confirm it's enabled, make note of IRQ:
    lspci -v | grep -A 10 "02:00.2"
    lspci -v | grep -A 10 "02:00.2"
    output:
    [root@localhost ~]# lspci -v | grep -A 10 "02:00.2"
    02:00.2 Parallel controller: Asix Electronics Corporation Device 9100 (prog-if 03 [IEEE1284])
            Subsystem: Device a000:2000
            Flags: fast devsel, IRQ 48
            I/O ports at e010 [size=8]
            I/O ports at e000 [size=8]
            Memory at fbb01000 (32-bit, non-prefetchable) [size=4K]
            Memory at fbb00000 (32-bit, non-prefetchable) [size=4K]
            Capabilities: [50] MSI: Enable- Count=1/8 Maskable- 64bit+
            Capabilities: [78] Power Management version 3
            Capabilities: [80] Express Legacy Endpoint, MSI 00
            Capabilities: [100] Advanced Error Reporting
    [root@localhost ~]# lspci -v | grep -A 10 "02:00.2"
    02:00.2 Parallel controller: Asix Electronics Corporation Device 9100 (prog-if 03 [IEEE1284])
            Subsystem: Device a000:2000
            Flags: fast devsel, IRQ 48
            I/O ports at e010 [size=8]
            I/O ports at e000 [size=8]
            Memory at fbb01000 (32-bit, non-prefetchable) [size=4K]
            Memory at fbb00000 (32-bit, non-prefetchable) [size=4K]
            Capabilities: [50] MSI: Enable- Count=1/8 Maskable- 64bit+
            Capabilities: [78] Power Management version 3
            Capabilities: [80] Express Legacy Endpoint, MSI 00
            Capabilities: [100] Advanced Error Reporting
  3. Remove old modprobe, load new modprobe:
    sudo modprobe -r parport_pc
    sudo modprobe parport_pc io=0xe010 irq=48
    sudo modprobe -r parport_pc
    sudo modprobe parport_pc io=0xe010 irq=48
  4. Make sure dmesg shows no issues:
    [root@localhost ~]# dmesg | grep parport
    [  276.465148] parport0: PC-style at 0xe010, irq 48 [PCSPP,TRISTATE,EPP]
    [root@localhost ~]#
    [root@localhost ~]# dmesg | grep parport
    [  276.465148] parport0: PC-style at 0xe010, irq 48 [PCSPP,TRISTATE,EPP]
    [root@localhost ~]#
  5. Check for parport0 in devices:
    [root@localhost ~]# ls /dev/parport0
    /dev/parport0
    [root@localhost ~]# ls /dev/parport0
    /dev/parport0
  6. Get details abourt parport0
    [root@localhost ~]# udevadm info -q path -n /dev/parport0
    /devices/platform/parport_pc.57360/ppdev/parport0
    [root@localhost ~]# udevadm info -a -p /devices/platform/parport_pc.57360/ppdev/parport0
    
    Udevadm info starts with the device specified by the devpath and then
    walks up the chain of parent devices. It prints for every device
    found, all possible attributes in the udev rules key format.
    A rule to match, can be composed by the attributes of the device
    and the attributes from one single parent device.
    
      looking at device '/devices/platform/parport_pc.57360/ppdev/parport0':
        KERNEL=="parport0"
        SUBSYSTEM=="ppdev"
        DRIVER==""
    
      looking at parent device '/devices/platform/parport_pc.57360':
        KERNELS=="parport_pc.57360"
        SUBSYSTEMS=="platform"
        DRIVERS=="parport_pc"
    
      looking at parent device '/devices/platform':
        KERNELS=="platform"
        SUBSYSTEMS==""
        DRIVERS==""
    
    [root@localhost ~]#
    [root@localhost ~]# udevadm info -q path -n /dev/parport0
    /devices/platform/parport_pc.57360/ppdev/parport0
    [root@localhost ~]# udevadm info -a -p /devices/platform/parport_pc.57360/ppdev/parport0
    
    Udevadm info starts with the device specified by the devpath and then
    walks up the chain of parent devices. It prints for every device
    found, all possible attributes in the udev rules key format.
    A rule to match, can be composed by the attributes of the device
    and the attributes from one single parent device.
    
      looking at device '/devices/platform/parport_pc.57360/ppdev/parport0':
        KERNEL=="parport0"
        SUBSYSTEM=="ppdev"
        DRIVER==""
    
      looking at parent device '/devices/platform/parport_pc.57360':
        KERNELS=="parport_pc.57360"
        SUBSYSTEMS=="platform"
        DRIVERS=="parport_pc"
    
      looking at parent device '/devices/platform':
        KERNELS=="platform"
        SUBSYSTEMS==""
        DRIVERS==""
    
    [root@localhost ~]#

24/05/2024 01:56

Apparently doing above was not enough. I tried changinging the code for MasterGM2/frontend.cpp

  /* configure parallel port */
  if (trigger_source == PP) {
    /* open trigger device */
    const char *fd_name = "/dev/trigger";
    printf("Connecting to [%s] ...", fd_name);

    fd_pp = open("/dev/trigger", O_RDWR); // for DAQ enable output
    if (fd_pp < 0) {
      cm_msg(MERROR, "frontend_init", "Error opening file [%s]", fd_name);
      return FE_ERR_HW;
    }
    printf(" done PP configure \n");

  }
  /* configure parallel port */
  if (trigger_source == PP) {
    /* open trigger device */
    const char *fd_name = "/dev/trigger";
    printf("Connecting to [%s] ...", fd_name);

    fd_pp = open("/dev/trigger", O_RDWR); // for DAQ enable output
    if (fd_pp < 0) {
      cm_msg(MERROR, "frontend_init", "Error opening file [%s]", fd_name);
      return FE_ERR_HW;
    }
    printf(" done PP configure \n");

  }

to

  /* configure parallel port */
  if (trigger_source == PP) {
    /* open trigger device */
    const char *fd_name = "/dev/parport0";
    printf("Connecting to [%s] ...", fd_name);

    fd_pp = open("/dev/parport0", O_RDWR); // for DAQ enable output
    if (fd_pp < 0) {
      cm_msg(MERROR, "frontend_init", "Error opening file [%s]", fd_name);
      return FE_ERR_HW;
    }
    printf(" done PP configure \n");

  }
  /* configure parallel port */
  if (trigger_source == PP) {
    /* open trigger device */
    const char *fd_name = "/dev/parport0";
    printf("Connecting to [%s] ...", fd_name);

    fd_pp = open("/dev/parport0", O_RDWR); // for DAQ enable output
    if (fd_pp < 0) {
      cm_msg(MERROR, "frontend_init", "Error opening file [%s]", fd_name);
      return FE_ERR_HW;
    }
    printf(" done PP configure \n");

  }

It didn't complain when initializing (but I'm pretty sure it wouldn't complain as long as you gave it a valid filepath at this point).

I was able to start a run but saw no events from the master (and no errors). Ending a run gave this error:

01:55:35.682 2024/05/24 [mhttpd,INFO] Run #230 stopped

01:55:32.464 2024/05/24 [MasterGM2,ERROR] [frontend.cpp:1312:end_of_run,ERROR] Error writing to parallel port

01:55:32.464 2024/05/24 [MasterGM2,INFO] End of Run: fills registered by all frontends match!

01:55:32.464 2024/05/24 [MasterGM2,INFO] End of Run: DC7 Triggers Received 16081  Count triggers 0

01:55:01.711 2024/05/24 [mhttpd,INFO] Run #230 started
01:55:35.682 2024/05/24 [mhttpd,INFO] Run #230 stopped

01:55:32.464 2024/05/24 [MasterGM2,ERROR] [frontend.cpp:1312:end_of_run,ERROR] Error writing to parallel port

01:55:32.464 2024/05/24 [MasterGM2,INFO] End of Run: fills registered by all frontends match!

01:55:32.464 2024/05/24 [MasterGM2,INFO] End of Run: DC7 Triggers Received 16081  Count triggers 0

01:55:01.711 2024/05/24 [mhttpd,INFO] Run #230 started

It seems apparent that poll( &pfds, 1, timeout) in this part of the code:

    if(trigger_source==PP){

      pfds.fd = fd_pp;
      pfds.events = POLLIN;

      int timeout = 10; 

      int ret = 0;
      ret = poll( &pfds, 1, timeout);

      if ( ret > 0 )
      {	    
    if(trigger_source==PP){

      pfds.fd = fd_pp;
      pfds.events = POLLIN;

      int timeout = 10; 

      int ret = 0;
      ret = poll( &pfds, 1, timeout);

      if ( ret > 0 )
      {	    

is returning less than 1 every time. I.e. the polling is probably timing out.


24/05/2024 02:15

To test out the paralllel port, I got chatGPT to write some C code (placed in /home/playground/parallel_port/parallel_port_test) that supposedly reads and writes to it:
parallel_port_test.c

#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <linux/ppdev.h>

int main() {
    int fd = open("/dev/parport0", O_RDWR);
    if (fd == -1) {
        perror("Cannot open /dev/parport0");
        return 1;
    }

    if (ioctl(fd, PPEXCL) < 0) {
        perror("Could not lock port");
        close(fd);
        return 1;
    }

    if (ioctl(fd, PPCLAIM) < 0) {
        perror("Could not claim port");
        close(fd);
        return 1;
    }

    char write_data = 0xFF; // Data to send
    printf("Writing data: 0x%02X\n", (unsigned char)write_data);
    if (ioctl(fd, PPWDATA, &write_data) < 0) {
        perror("Could not write data");
        close(fd);
        return 1;
    } else {
        printf("Data written successfully\n");
    }

    char read_data;
    if (ioctl(fd, PPRSTATUS, &read_data) < 0) {
        perror("Could not read data");
        close(fd);
        return 1;
    }

    printf("Data read: 0x%02X\n", read_data);

    if (read_data == write_data) {
        printf("Readback matches write operation.\n");
    } else {
        printf("Readback does not match write operation.\n");
    }

    ioctl(fd, PPRELEASE);
    close(fd);
    return 0;
}
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <linux/ppdev.h>

int main() {
    int fd = open("/dev/parport0", O_RDWR);
    if (fd == -1) {
        perror("Cannot open /dev/parport0");
        return 1;
    }

    if (ioctl(fd, PPEXCL) < 0) {
        perror("Could not lock port");
        close(fd);
        return 1;
    }

    if (ioctl(fd, PPCLAIM) < 0) {
        perror("Could not claim port");
        close(fd);
        return 1;
    }

    char write_data = 0xFF; // Data to send
    printf("Writing data: 0x%02X\n", (unsigned char)write_data);
    if (ioctl(fd, PPWDATA, &write_data) < 0) {
        perror("Could not write data");
        close(fd);
        return 1;
    } else {
        printf("Data written successfully\n");
    }

    char read_data;
    if (ioctl(fd, PPRSTATUS, &read_data) < 0) {
        perror("Could not read data");
        close(fd);
        return 1;
    }

    printf("Data read: 0x%02X\n", read_data);

    if (read_data == write_data) {
        printf("Readback matches write operation.\n");
    } else {
        printf("Readback does not match write operation.\n");
    }

    ioctl(fd, PPRELEASE);
    close(fd);
    return 0;
}

Nothing fails, however, the output indicates that the write wasn't succesfful:

[root@localhost parallel_port_test]# ./parallel_port_test
Writing data: 0xFF
Data written successfully
Data read: 0x38
Readback does not match write operation.
[root@localhost parallel_port_test]# ./parallel_port_test
Writing data: 0xFF
Data written successfully
Data read: 0x38
Readback does not match write operation.

24/05/2024 02:34

I got ChatGPT to make me two other test scripts, both failed similarly:
parallel_port_write.c

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <linux/ppdev.h>
#include <limits.h> // Include limits.h for UCHAR_MAX

#define PORT_ADDRESS "/dev/parport0"
#define DEFAULT_DATA 0xFF

int main(int argc, char *argv[]) {
    // Data to write to the parallel port (default: 0xFF)
    unsigned char write_data = DEFAULT_DATA;

    // Check if a command-line argument is provided
    if (argc > 1) {
        // Convert the argument to an integer
        unsigned long int parsed_data = strtoul(argv[1], NULL, 0);
        
        // Check if conversion was successful
        if (parsed_data <= UCHAR_MAX) {
            write_data = (unsigned char)parsed_data;
        } else {
            fprintf(stderr, "Error: Invalid data argument. Using default: 0x%02X\n", DEFAULT_DATA);
        }
    }

    // Open the parallel port
    int fd_pp = open(PORT_ADDRESS, O_RDWR);
    if (fd_pp == -1) {
        perror("Error opening parallel port");
        return 1;
    }

    // Write data to the parallel port
    printf("Writing data to parallel port: 0x%02X\n", write_data);
    ssize_t bytes_written = write(fd_pp, &write_data, sizeof(write_data));
    if (bytes_written < 0) {
        perror("Error writing data to parallel port");
        close(fd_pp);
        return 1;
    } else if (bytes_written != sizeof(write_data)) {
        fprintf(stderr, "Error: Incomplete write to parallel port\n");
        close(fd_pp);
        return 1;
    }

    printf("Data written successfully\n");

    // Close the parallel port
    close(fd_pp);
    return 0;
}
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <linux/ppdev.h>
#include <limits.h> // Include limits.h for UCHAR_MAX

#define PORT_ADDRESS "/dev/parport0"
#define DEFAULT_DATA 0xFF

int main(int argc, char *argv[]) {
    // Data to write to the parallel port (default: 0xFF)
    unsigned char write_data = DEFAULT_DATA;

    // Check if a command-line argument is provided
    if (argc > 1) {
        // Convert the argument to an integer
        unsigned long int parsed_data = strtoul(argv[1], NULL, 0);
        
        // Check if conversion was successful
        if (parsed_data <= UCHAR_MAX) {
            write_data = (unsigned char)parsed_data;
        } else {
            fprintf(stderr, "Error: Invalid data argument. Using default: 0x%02X\n", DEFAULT_DATA);
        }
    }

    // Open the parallel port
    int fd_pp = open(PORT_ADDRESS, O_RDWR);
    if (fd_pp == -1) {
        perror("Error opening parallel port");
        return 1;
    }

    // Write data to the parallel port
    printf("Writing data to parallel port: 0x%02X\n", write_data);
    ssize_t bytes_written = write(fd_pp, &write_data, sizeof(write_data));
    if (bytes_written < 0) {
        perror("Error writing data to parallel port");
        close(fd_pp);
        return 1;
    } else if (bytes_written != sizeof(write_data)) {
        fprintf(stderr, "Error: Incomplete write to parallel port\n");
        close(fd_pp);
        return 1;
    }

    printf("Data written successfully\n");

    // Close the parallel port
    close(fd_pp);
    return 0;
}

parallel_port_poll.c

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <sys/poll.h> // Include poll header

#define PORT_ADDRESS "/dev/parport0" // Adjust the port address as per your system

int main() {
    // Open the parallel port
    int fd_pp = open(PORT_ADDRESS, O_RDONLY); // Open in read-only mode for polling
    if (fd_pp == -1) {
        perror("Cannot open parallel port");
        return 1;
    }

    // Set up the pollfd structure
    struct pollfd pfds;
    pfds.fd = fd_pp; // File descriptor to poll
    pfds.events = POLLIN; // Events to monitor (in this case, POLLIN for input data)

    int timeout = 10; // Timeout value in milliseconds

    // Perform polling
    int ret = poll(&pfds, 1, timeout);
    if (ret == -1) {
        perror("poll");
        close(fd_pp);
        return 1;
    } else if (ret == 0) {
        printf("Timeout occurred\n");
    } else {
        // Check if POLLIN event occurred
        if (pfds.revents & POLLIN) {
            printf("Data available for reading from parallel port\n");
            // Read data from the parallel port if needed
            unsigned char data;
            if (read(fd_pp, &data, sizeof(data)) < 0) {
                perror("Error reading data from parallel port");
            } else {
                printf("Data read from parallel port: 0x%02X\n", data);
            }
        } else {
            printf("Unexpected event occurred\n");
        }
    }

    // Close the parallel port
    close(fd_pp);
    return 0;
}
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <sys/poll.h> // Include poll header

#define PORT_ADDRESS "/dev/parport0" // Adjust the port address as per your system

int main() {
    // Open the parallel port
    int fd_pp = open(PORT_ADDRESS, O_RDONLY); // Open in read-only mode for polling
    if (fd_pp == -1) {
        perror("Cannot open parallel port");
        return 1;
    }

    // Set up the pollfd structure
    struct pollfd pfds;
    pfds.fd = fd_pp; // File descriptor to poll
    pfds.events = POLLIN; // Events to monitor (in this case, POLLIN for input data)

    int timeout = 10; // Timeout value in milliseconds

    // Perform polling
    int ret = poll(&pfds, 1, timeout);
    if (ret == -1) {
        perror("poll");
        close(fd_pp);
        return 1;
    } else if (ret == 0) {
        printf("Timeout occurred\n");
    } else {
        // Check if POLLIN event occurred
        if (pfds.revents & POLLIN) {
            printf("Data available for reading from parallel port\n");
            // Read data from the parallel port if needed
            unsigned char data;
            if (read(fd_pp, &data, sizeof(data)) < 0) {
                perror("Error reading data from parallel port");
            } else {
                printf("Data read from parallel port: 0x%02X\n", data);
            }
        } else {
            printf("Unexpected event occurred\n");
        }
    }

    // Close the parallel port
    close(fd_pp);
    return 0;
}

With outputs:

[root@localhost parallel_port_test]# ./parallel_port_write 0xAA
Writing data to parallel port: 0xAA
Error writing data to parallel port: Invalid argument
[root@localhost parallel_port_test]# ./parallel_port_poll
Timeout occurred
[root@localhost parallel_port_test]#
[root@localhost parallel_port_test]# ./parallel_port_write 0xAA
Writing data to parallel port: 0xAA
Error writing data to parallel port: Invalid argument
[root@localhost parallel_port_test]# ./parallel_port_poll
Timeout occurred
[root@localhost parallel_port_test]#

These two files try to more closely mimic the function calls present in MasterGM2/frontend.cpp


24/05/2024 02:40

I'm fairly confident /dev/parport0 is the correct path because it dissapears with command:

sudo modprobe -r parport_pc
sudo modprobe -r parport_pc

and reappears with command:

sudo modprobe parport_pc io=0xe010 irq=48
sudo modprobe parport_pc io=0xe010 irq=48

Interestingly, I don't need to include irq=48 to make /dev/parport0 appear. I'm not sure if this is meaningful.

In any event, the result of the scripts and launching the frontends is no different whether I use

sudo modprobe parport_pc io=0xe010 irq=48
sudo modprobe parport_pc io=0xe010 irq=48

or

sudo modprobe parport_pc io=0xe010
sudo modprobe parport_pc io=0xe010

to load the module.

But I do notice MasterGM2/frontend causes this output when loading the module in the second case:

Message from syslogd@localhost at May 24 02:50:47 ...
 kernel:Disabling IRQ #18
Message from syslogd@localhost at May 24 02:50:47 ...
 kernel:Disabling IRQ #18

it occurs system wide, I saw it occur in another shell session ssh'd into the computer that was not running the frontend.
I also notice this error at the beginning of a run:

Error: Write to parallel port failed! Data 0x007f
[MasterGM2,ERROR] [frontend.cpp:1112:begin_of_runfe_loop,ERROR] Error writing to parallel port
begin_of_run: write datum  0x7f
Error: Write to parallel port failed! Data 0x007f
[MasterGM2,ERROR] [frontend.cpp:1112:begin_of_runfe_loop,ERROR] Error writing to parallel port
begin_of_run: write datum  0x7f

That is not critical to the run starting. It happens whether I do sudo modprobe parport_pc io=0xe010 irq=48 or sudo modprobe parport_pc io=0xe010